using System;
using System.IO;
using System.Collections.Generic;
using System.Xml;
using System.Drawing;
using AvantGameMachine.FileSystem;
using AvantGameMachine.Graphics;

namespace AvantGameMachine.DataManagement
{
	public class StructuredData
	{
		protected List<StructuredData> children = new List<StructuredData>();
		private StructuredData parent = null;
		private object data = new object();
		private string name="";
		private Dictionary<string,string> properties = new Dictionary<string, string>();

		public StructuredData ()
		{
		}
		
		public StructuredData(string _name) {
			this.name = _name;
		}
		
		public Dictionary<string,string> Properties {
			get { return properties; }
		}

		public static StructuredData Create(string file) {
			XmlDocument xDoc = new XmlDocument();
			try {
				xDoc.Load(file);
				StructuredData newS = new StructuredData();
				newS.AddXMLNode(xDoc.ChildNodes[0],newS);
				return newS;
			} catch (Exception e) {
				Console.WriteLine(e);
			}
			return null;
        }
		
		private static string Convert(byte[] _source) {
			System.Text.ASCIIEncoding codifier = new System.Text.ASCIIEncoding();
			return codifier.GetString(_source);				
		}

		public static StructuredData Create(byte[] _input) {
			XmlDocument xDoc = new XmlDocument();
			try {
				string data = Convert(_input);
				xDoc.LoadXml(data);
				StructuredData newS = new StructuredData();
				newS.AddXMLNode(xDoc.ChildNodes[0],newS);
				return newS;
			} catch (Exception e) {
				Console.WriteLine(e);
			}
			return null;
		}
		
		public static StructuredData Create(Address _address) {
			FileInformation fInfo = (FileInformation)AGMPlatform.FSManager.GetFile(_address);
			if (fInfo != null) {
				StructuredData data = StructuredData.Create((byte[])fInfo.GetData());
				return data;
			}
			return null;
		}


		private void SetParent(StructuredData _parent) {
			this.parent = _parent;
		}

		private void AddXMLNode(XmlNode xmlnode,StructuredData _parent) {
			StructuredData node = new StructuredData();
			Name = xmlnode.Name;
			Data = xmlnode.InnerText;
			this.SetParent(_parent);

			// Read all the properties;
			XmlAttributeCollection ac = xmlnode.Attributes;
			foreach (XmlAttribute attr in ac) {
				properties.Add(attr.Name,attr.Value);
			}
			
			// Add the children.
			foreach (XmlNode nodechild in xmlnode.ChildNodes) {
				if (nodechild.Name != "#text" && nodechild.Name != "#comment") {
					StructuredData data = new StructuredData();
					data.AddXMLNode(nodechild,this);
					children.Add(data);
				}
			}
		}
		
		public void AddNode(StructuredData _data) {
			_data.SetParent(this);
			children.Add(_data);
		}

		public StructuredData GetNodeFromAddress(Address _addres,int _index) {
			StructuredData[] data = GetNodeFromAddress(_addres);
			if (data.Length > _index) {
				return data[_index];
			}
			return null;
		}

		public bool DeleteAddressChildren(Address _address)
        {
			StructuredData[] children = GetNodeFromAddress(_address);
            foreach (StructuredData data in children)
            {
            }

            return true;
		}

		public StructuredData[] GetNodeFromAddress(Address _address) {
			List<StructuredData> list = new List<StructuredData>();
			
			if (_address.GetLen() > 0)
            {
				if (_address.GetValueAt(0) == name)
                {
					if (_address.GetLen() > 1)
                    {
						foreach (StructuredData child in children)
                        {
							StructuredData[] data = child.GetNodeFromAddress(_address.GetRange(1,-1));
							if (data != null && data.Length > 0)
                            {
								foreach (StructuredData dt in data)
                                {
									list.Add(dt);
								}
								return list.ToArray();
							}
						}
						return new StructuredData[] {};
					}
                    else
                    {
						return new StructuredData[] {this};
					}
				} 
                else
                {
					return new StructuredData[] {};
				}
			} 
            else
            {
				return new StructuredData[] {this};
			}
		}
		
		public override string ToString ()
		{
			return getStringWithLevel(0);
		}

		public string getStringWithLevel(int _level) {
			string me = "";
			for (int i=0;i<_level;i++)
				me += "  ";
			
			me += "<" + Name;

			foreach (string propName in properties.Keys) {
				string val = properties[propName];
				me += " " + propName + "=\"" + val + "\"";
			}

			me += ">";

			bool childs = children.Count > 0;
			
			if (childs)
				me += "\n";
				
			foreach (StructuredData data in children) {
				childs = true;
				me += data.getStringWithLevel(_level+1);
			}

            if (!childs)
            {
                if (Data.GetType().IsAssignableFrom(typeof(IStructuredDataConvertible)))
                {
                    me += ((IStructuredDataConvertible)Data).Convert().ToString();
                }
                else
                {
                    me += Data.ToString();
                }
            }
            else
            {
                for (int i = 0; i < _level; i++)
                    me += "  ";
            }

			me += "</" + Name + ">\n";
				
			return me;
		}

		public int GetTextAsNumber(int _defaultValue) {
			int result;
			bool ok = int.TryParse((string)data,out result);
			return ok?result:_defaultValue;
		}

        public float GetTextAsFloat(float _defaultValue)
        {
            float result;
            bool ok = float.TryParse((string)data, out result);
            return ok ? result : _defaultValue;
        }

		public Rectangle GetRectangle(Rectangle _default) {
			Point pt = GetPoint(_default.Location);
			Size sz = GetSize(_default.Size);
			return new Rectangle(pt,sz);
		}

		public Point GetPoint(Point _default) {
			StructuredData childx = GetNodeFromAddress(new Address(this.name+"/X"),0);
			StructuredData childy = GetNodeFromAddress(new Address(this.name+"/Y"),0);

			int x = _default.X;
			if (childx != null)
				x = childx.GetTextAsNumber(_default.X);

			int y = _default.Y;
			if (childy != null)
				y = childy.GetTextAsNumber(_default.Y);
			return new Point(x,y);
		}

        public PosVector GetVector(PosVector _default)
        {
            StructuredData childx = GetNodeFromAddress(new Address(this.name + "/X"), 0);
            StructuredData childy = GetNodeFromAddress(new Address(this.name + "/Y"), 0);
            StructuredData childz = GetNodeFromAddress(new Address(this.name + "/Z"), 0);

            float x = _default.X;
            if (childx != null)
                x = childx.GetTextAsFloat(_default.X);

            float y = _default.Y;
            if (childy != null)
                y = childy.GetTextAsFloat(_default.Y);

            float z = _default.Z;
            if (childz != null)
                y = childy.GetTextAsFloat(_default.Z);

            return new PosVector(x, y, z);
        }

		public Size GetSize(Size _default) {
			StructuredData childw = GetNodeFromAddress(new Address(this.name+"/Width"),0);
			StructuredData childh = GetNodeFromAddress(new Address(this.name+"/Height"),0);
			
			int w = _default.Width;
			if (childw != null)
				w = childw.GetTextAsNumber(_default.Width);
			
			int h = _default.Height;
			if (childh != null)
				h = childh.GetTextAsNumber(_default.Height);

			return new Size(w,h);
		}
		
		public bool GetBool(bool _default) {
			bool result;
			bool ok = Boolean.TryParse((string)data,out result);
			return ok?result:_default;
		}
		/*
		public Color GetColor(Color _default) {
			StructuredData childr = GetNodeFromAddress(new Address(this.name+"/R"),0);
			StructuredData childg = GetNodeFromAddress(new Address(this.name+"/G"),0);
			StructuredData childb = GetNodeFromAddress(new Address(this.name+"/B"),0);
			StructuredData childa = GetNodeFromAddress(new Address(this.name+"/A"),0);
			int r = childr.GetTextAsNumber(_default.R);
			int g = childg.GetTextAsNumber(_default.G);
			int b = childb.GetTextAsNumber(_default.B);
			int a = childa.GetTextAsNumber(_default.A);
			return Color.FromArgb(a,r,g,b);
		*/
		
		public string GetText(string _default) {
			return (string)Data.ToString();
		}

		public object Data {
			get {	return data;	}
			set {	data = value;	}
		}
		
		public string Name {
			get {	return name;	}
			set {	name = value;	}
		}
		
		public List<StructuredData> Children {
			get { return this.children; }
		}
        /*
        public IRenderizableObject ReadScene(IRenderizableObject _parent)
        {

        }*/
	}
}
